/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

var zrUtil = require("static/plugins/js/zrender/lib/core/util");

var graphic = require("../../util/graphic");

var Model = require("../../model/Model");

var AxisView = require("./AxisView");

var AxisBuilder = require("./AxisBuilder");

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
var elementList = [
    "axisLine",
    "axisLabel",
    "axisTick",
    "minorTick",
    "splitLine",
    "minorSplitLine",
    "splitArea",
];

function getAxisLineShape(polar, rExtent, angle) {
    rExtent[1] > rExtent[0] && (rExtent = rExtent.slice().reverse());
    var start = polar.coordToPoint([rExtent[0], angle]);
    var end = polar.coordToPoint([rExtent[1], angle]);
    return {
        x1: start[0],
        y1: start[1],
        x2: end[0],
        y2: end[1],
    };
}

function getRadiusIdx(polar) {
    var radiusAxis = polar.getRadiusAxis();
    return radiusAxis.inverse ? 0 : 1;
} // Remove the last tick which will overlap the first tick

function fixAngleOverlap(list) {
    var firstItem = list[0];
    var lastItem = list[list.length - 1];

    if (
        firstItem &&
        lastItem &&
        Math.abs(Math.abs(firstItem.coord - lastItem.coord) - 360) < 1e-4
    ) {
        list.pop();
    }
}

var _default = AxisView.extend({
    type: "angleAxis",
    axisPointerClass: "PolarAxisPointer",
    render: function (angleAxisModel, ecModel) {
        this.group.removeAll();

        if (!angleAxisModel.get("show")) {
            return;
        }

        var angleAxis = angleAxisModel.axis;
        var polar = angleAxis.polar;
        var radiusExtent = polar.getRadiusAxis().getExtent();
        var ticksAngles = angleAxis.getTicksCoords();
        var minorTickAngles = angleAxis.getMinorTicksCoords();
        var labels = zrUtil.map(
            angleAxis.getViewLabels(),
            function (labelItem) {
                var labelItem = zrUtil.clone(labelItem);
                labelItem.coord = angleAxis.dataToCoord(labelItem.tickValue);
                return labelItem;
            }
        );
        fixAngleOverlap(labels);
        fixAngleOverlap(ticksAngles);
        zrUtil.each(
            elementList,
            function (name) {
                if (
                    angleAxisModel.get(name + ".show") &&
                    (!angleAxis.scale.isBlank() || name === "axisLine")
                ) {
                    this["_" + name](
                        angleAxisModel,
                        polar,
                        ticksAngles,
                        minorTickAngles,
                        radiusExtent,
                        labels
                    );
                }
            },
            this
        );
    },

    /**
     * @private
     */
    _axisLine: function (
        angleAxisModel,
        polar,
        ticksAngles,
        minorTickAngles,
        radiusExtent
    ) {
        var lineStyleModel = angleAxisModel.getModel("axisLine.lineStyle"); // extent id of the axis radius (r0 and r)

        var rId = getRadiusIdx(polar);
        var r0Id = rId ? 0 : 1;
        var shape;

        if (radiusExtent[r0Id] === 0) {
            shape = new graphic.Circle({
                shape: {
                    cx: polar.cx,
                    cy: polar.cy,
                    r: radiusExtent[rId],
                },
                style: lineStyleModel.getLineStyle(),
                z2: 1,
                silent: true,
            });
        } else {
            shape = new graphic.Ring({
                shape: {
                    cx: polar.cx,
                    cy: polar.cy,
                    r: radiusExtent[rId],
                    r0: radiusExtent[r0Id],
                },
                style: lineStyleModel.getLineStyle(),
                z2: 1,
                silent: true,
            });
        }

        shape.style.fill = null;
        this.group.add(shape);
    },

    /**
     * @private
     */
    _axisTick: function (
        angleAxisModel,
        polar,
        ticksAngles,
        minorTickAngles,
        radiusExtent
    ) {
        var tickModel = angleAxisModel.getModel("axisTick");
        var tickLen =
            (tickModel.get("inside") ? -1 : 1) * tickModel.get("length");
        var radius = radiusExtent[getRadiusIdx(polar)];
        var lines = zrUtil.map(ticksAngles, function (tickAngleItem) {
            return new graphic.Line({
                shape: getAxisLineShape(
                    polar,
                    [radius, radius + tickLen],
                    tickAngleItem.coord
                ),
            });
        });
        this.group.add(
            graphic.mergePath(lines, {
                style: zrUtil.defaults(
                    tickModel.getModel("lineStyle").getLineStyle(),
                    {
                        stroke: angleAxisModel.get("axisLine.lineStyle.color"),
                    }
                ),
            })
        );
    },

    /**
     * @private
     */
    _minorTick: function (
        angleAxisModel,
        polar,
        tickAngles,
        minorTickAngles,
        radiusExtent
    ) {
        if (!minorTickAngles.length) {
            return;
        }

        var tickModel = angleAxisModel.getModel("axisTick");
        var minorTickModel = angleAxisModel.getModel("minorTick");
        var tickLen =
            (tickModel.get("inside") ? -1 : 1) * minorTickModel.get("length");
        var radius = radiusExtent[getRadiusIdx(polar)];
        var lines = [];

        for (var i = 0; i < minorTickAngles.length; i++) {
            for (var k = 0; k < minorTickAngles[i].length; k++) {
                lines.push(
                    new graphic.Line({
                        shape: getAxisLineShape(
                            polar,
                            [radius, radius + tickLen],
                            minorTickAngles[i][k].coord
                        ),
                    })
                );
            }
        }

        this.group.add(
            graphic.mergePath(lines, {
                style: zrUtil.defaults(
                    minorTickModel.getModel("lineStyle").getLineStyle(),
                    zrUtil.defaults(tickModel.getLineStyle(), {
                        stroke: angleAxisModel.get("axisLine.lineStyle.color"),
                    })
                ),
            })
        );
    },

    /**
     * @private
     */
    _axisLabel: function (
        angleAxisModel,
        polar,
        ticksAngles,
        minorTickAngles,
        radiusExtent,
        labels
    ) {
        var rawCategoryData = angleAxisModel.getCategories(true);
        var commonLabelModel = angleAxisModel.getModel("axisLabel");
        var labelMargin = commonLabelModel.get("margin");
        var triggerEvent = angleAxisModel.get("triggerEvent"); // Use length of ticksAngles because it may remove the last tick to avoid overlapping

        zrUtil.each(
            labels,
            function (labelItem, idx) {
                var labelModel = commonLabelModel;
                var tickValue = labelItem.tickValue;
                var r = radiusExtent[getRadiusIdx(polar)];
                var p = polar.coordToPoint([r + labelMargin, labelItem.coord]);
                var cx = polar.cx;
                var cy = polar.cy;
                var labelTextAlign =
                    Math.abs(p[0] - cx) / r < 0.3
                        ? "center"
                        : p[0] > cx
                        ? "left"
                        : "right";
                var labelTextVerticalAlign =
                    Math.abs(p[1] - cy) / r < 0.3
                        ? "middle"
                        : p[1] > cy
                        ? "top"
                        : "bottom";

                if (
                    rawCategoryData &&
                    rawCategoryData[tickValue] &&
                    rawCategoryData[tickValue].textStyle
                ) {
                    labelModel = new Model(
                        rawCategoryData[tickValue].textStyle,
                        commonLabelModel,
                        commonLabelModel.ecModel
                    );
                }

                var textEl = new graphic.Text({
                    silent: AxisBuilder.isLabelSilent(angleAxisModel),
                });
                this.group.add(textEl);
                graphic.setTextStyle(textEl.style, labelModel, {
                    x: p[0],
                    y: p[1],
                    textFill:
                        labelModel.getTextColor() ||
                        angleAxisModel.get("axisLine.lineStyle.color"),
                    text: labelItem.formattedLabel,
                    textAlign: labelTextAlign,
                    textVerticalAlign: labelTextVerticalAlign,
                }); // Pack data for mouse event

                if (triggerEvent) {
                    textEl.eventData =
                        AxisBuilder.makeAxisEventDataBase(angleAxisModel);
                    textEl.eventData.targetType = "axisLabel";
                    textEl.eventData.value = labelItem.rawLabel;
                }
            },
            this
        );
    },

    /**
     * @private
     */
    _splitLine: function (
        angleAxisModel,
        polar,
        ticksAngles,
        minorTickAngles,
        radiusExtent
    ) {
        var splitLineModel = angleAxisModel.getModel("splitLine");
        var lineStyleModel = splitLineModel.getModel("lineStyle");
        var lineColors = lineStyleModel.get("color");
        var lineCount = 0;
        lineColors = lineColors instanceof Array ? lineColors : [lineColors];
        var splitLines = [];

        for (var i = 0; i < ticksAngles.length; i++) {
            var colorIndex = lineCount++ % lineColors.length;
            splitLines[colorIndex] = splitLines[colorIndex] || [];
            splitLines[colorIndex].push(
                new graphic.Line({
                    shape: getAxisLineShape(
                        polar,
                        radiusExtent,
                        ticksAngles[i].coord
                    ),
                })
            );
        } // Simple optimization
        // Batching the lines if color are the same

        for (var i = 0; i < splitLines.length; i++) {
            this.group.add(
                graphic.mergePath(splitLines[i], {
                    style: zrUtil.defaults(
                        {
                            stroke: lineColors[i % lineColors.length],
                        },
                        lineStyleModel.getLineStyle()
                    ),
                    silent: true,
                    z: angleAxisModel.get("z"),
                })
            );
        }
    },

    /**
     * @private
     */
    _minorSplitLine: function (
        angleAxisModel,
        polar,
        ticksAngles,
        minorTickAngles,
        radiusExtent
    ) {
        if (!minorTickAngles.length) {
            return;
        }

        var minorSplitLineModel = angleAxisModel.getModel("minorSplitLine");
        var lineStyleModel = minorSplitLineModel.getModel("lineStyle");
        var lines = [];

        for (var i = 0; i < minorTickAngles.length; i++) {
            for (var k = 0; k < minorTickAngles[i].length; k++) {
                lines.push(
                    new graphic.Line({
                        shape: getAxisLineShape(
                            polar,
                            radiusExtent,
                            minorTickAngles[i][k].coord
                        ),
                    })
                );
            }
        }

        this.group.add(
            graphic.mergePath(lines, {
                style: lineStyleModel.getLineStyle(),
                silent: true,
                z: angleAxisModel.get("z"),
            })
        );
    },

    /**
     * @private
     */
    _splitArea: function (
        angleAxisModel,
        polar,
        ticksAngles,
        minorTickAngles,
        radiusExtent
    ) {
        if (!ticksAngles.length) {
            return;
        }

        var splitAreaModel = angleAxisModel.getModel("splitArea");
        var areaStyleModel = splitAreaModel.getModel("areaStyle");
        var areaColors = areaStyleModel.get("color");
        var lineCount = 0;
        areaColors = areaColors instanceof Array ? areaColors : [areaColors];
        var splitAreas = [];
        var RADIAN = Math.PI / 180;
        var prevAngle = -ticksAngles[0].coord * RADIAN;
        var r0 = Math.min(radiusExtent[0], radiusExtent[1]);
        var r1 = Math.max(radiusExtent[0], radiusExtent[1]);
        var clockwise = angleAxisModel.get("clockwise");

        for (var i = 1; i < ticksAngles.length; i++) {
            var colorIndex = lineCount++ % areaColors.length;
            splitAreas[colorIndex] = splitAreas[colorIndex] || [];
            splitAreas[colorIndex].push(
                new graphic.Sector({
                    shape: {
                        cx: polar.cx,
                        cy: polar.cy,
                        r0: r0,
                        r: r1,
                        startAngle: prevAngle,
                        endAngle: -ticksAngles[i].coord * RADIAN,
                        clockwise: clockwise,
                    },
                    silent: true,
                })
            );
            prevAngle = -ticksAngles[i].coord * RADIAN;
        } // Simple optimization
        // Batching the lines if color are the same

        for (var i = 0; i < splitAreas.length; i++) {
            this.group.add(
                graphic.mergePath(splitAreas[i], {
                    style: zrUtil.defaults(
                        {
                            fill: areaColors[i % areaColors.length],
                        },
                        areaStyleModel.getAreaStyle()
                    ),
                    silent: true,
                })
            );
        }
    },
});

module.exports = _default;
